package demo.java.util.concurrent.atomic;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.LongBinaryOperator;
import java.util.stream.IntStream;

import org.junit.Test;

import demo.java.lang.ThreadDemo;

/**
 * <h1>Atomic 关键字</h1> 可以使基本数据类型以原子的方式实现自增自减等操作。
 * <p>
 * 本质上，原子操作严重依赖于比较与交换（CAS），它是由多数现代CPU直接支持的原子指令。这些指令通常比同步块要快。
 * 所以在只需要并发修改单个可变变量的情况下，我建议你优先使用原子类，而不是锁。
 */
public class AtomicDemo {

    public static void main(String[] args) {
    }

    /**
     * 过使用AtomicInteger代替Integer，我们就能线程安全地并发增加数值，而不需要同步访问变量。
     * incrementAndGet()方法是原子操作，所以我们可以在多个线程中安全调用它。
     */
    @Test
    public void testAtomicInteger() {
        AtomicInteger atomicInt = new AtomicInteger(0);
        ExecutorService executor = Executors.newFixedThreadPool(2);
        IntStream.range(0, 1000).forEach(i -> executor.submit(atomicInt::incrementAndGet));
        ThreadDemo.safeSleep(1_000L);
        executor.shutdown();
        System.out.println(atomicInt.get()); // => 1000

        IntStream.range(0, 1000).forEach(i -> {
            Runnable task = () -> atomicInt.updateAndGet(n -> n + 2);
            executor.submit(task);
        });

        IntStream.range(0, 1000).forEach(i -> {
            Runnable task = () -> atomicInt.accumulateAndGet(i, (n, m) -> n + m);
            executor.submit(task);
        });
    }

    @Test
    public void testAtomicLong() {
        AtomicLong count = new AtomicLong();
        System.out.println(count);
        count = new AtomicLong(0);
        System.out.println(count);
        long l = count.incrementAndGet();
        System.out.println(l);
        System.out.println(count.get());
        count.addAndGet(107);
        System.out.println(count.get());
        count.compareAndSet(107, 100);
        System.out.println(count.get());
        count.decrementAndGet();
        System.out.println(count.get());
        l = count.getAndIncrement();
        System.out.println(l);
        System.out.println(count);
    }

    /**
     * LongAdder是AtomicLong的替代，用于向某个数值连续添加值。
     * <p>
     * LongAdder提供了add()和increment()方法，就像原子数值类一样，同样是线程安全的。
     * 但是这个类在内部维护一系列变量来减少线程之间的争用，而不是求和计算单一结果。
     * 实际的结果可以通过调用sum()或sumThenReset()来获取。
     * <p>
     * 当多线程的更新比读取更频繁时，这个类通常比原子数值类性能更好。
     * 这种情况在抓取统计数据时经常出现，例如，你希望统计Web服务器上请求的数量。
     * LongAdder缺点是较高的内存开销，因为它在内存中储存了一系列变量。
     */
    @Test
    public void testLongAdder() {
        LongAdder adder = new LongAdder();

        ExecutorService executor = Executors.newFixedThreadPool(2);

        IntStream.range(0, 1000).forEach(i -> executor.submit(adder::increment));

        ThreadDemo.safeSleep(1_000L);
        executor.shutdown();
        System.out.println(adder.sumThenReset()); // => 1000
    }

    /**
     * LongAccumulator是LongAdder的更通用的版本。
     * LongAccumulator以类型为LongBinaryOperatorlambda表达式构建，而不是仅仅执行加法操作，
     */
    @Test
    public void testLongAccumulator() {
        LongBinaryOperator op = (x, y) -> 2 * x + y;

        LongAccumulator accumulator = new LongAccumulator(op, 1L);

        ExecutorService executor = Executors.newFixedThreadPool(2);

        IntStream.range(0, 10).forEach(i -> executor.submit(() -> accumulator.accumulate(i)));

        ThreadDemo.safeSleep(1_000L);
        executor.shutdown();
        System.out.println(accumulator.getThenReset()); // => 2539
    }

}
